import * as THREE from 'three';
import CameraControls from 'camera-controls';
import { CSS3DRenderer } from "three/examples/jsm/renderers/CSS3DRenderer";
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer';
import {TransformControls} from "three/examples/jsm/controls/TransformControls";
import {SetPositionCommand} from "@/core/commands/SetPositionCommand";
import {SetRotationCommand} from "@/core/commands/SetRotationCommand";
import {SetScaleCommand} from "@/core/commands/SetScaleCommand";
import {useDispatchSignal} from "@/hooks/useSignal";
import {GRID_COLORS_DARK, GRID_COLORS_LIGHT} from "@/utils/common/constant";
import {getMousePosition} from "@/utils/common/scenes";
import {ViewportEffect} from "@/core/Viewport.Effect";
import ViewCube from "@/core/Viewport.Cube";
import {XR} from '@/core/Viewport.XR';
import {ViewportSignals} from "@/core/Viewport.Signals";
import {ShaderMaterialManager} from "@/core/shaderMaterial/ShaderMaterialManager";
import {Package} from "@/core/loader/Package";

CameraControls.install({
    THREE: {
        Vector2: THREE.Vector2,       // 2D向量，用于表示2D坐标或2D方向
        Vector3: THREE.Vector3,       // 3D向量，用于表示3D坐标、位置或方向
        Vector4: THREE.Vector4,       // 4D向量，主要用于齐次坐标系统
        Quaternion: THREE.Quaternion, // 四元数，用于表示3D旋转
        Matrix4: THREE.Matrix4,       // 4x4矩阵，用于3D变换（如位移、旋转、缩放）
        Spherical: THREE.Spherical,   // 球坐标系，用于表示球面上的点
        Box3: THREE.Box3,            // 3D包围盒，用于碰撞检测和物体边界计算
        Sphere: THREE.Sphere,        // 球体，用于包围球计算和碰撞检测
        Raycaster: THREE.Raycaster,  // 射线投射器，用于鼠标拾取和碰撞检测
    }
});

const onDownPosition = new THREE.Vector2();
const onUpPosition = new THREE.Vector2();
const onDoubleClickPosition = new THREE.Vector2();

let mouseUpFn;

// animations
let prevActionsInUse = 0;
const clock = new THREE.Clock();

// 计算帧时
let startTime = 0;
let endTime = 0;

export class Viewport {
    container: HTMLDivElement; // 容器DOM元素
    scene: THREE.Scene; // Three.js场景对象
    camera: THREE.PerspectiveCamera; // Three.js场景对象
    private sceneHelpers: THREE.Scene; // 辅助场景(用于显示网格、控制器等)
    renderer: THREE.WebGLRenderer | undefined; // WebGL渲染器
    css3DRenderer: CSS3DRenderer = new CSS3DRenderer(); // CSS 3D渲染器
    css2DRenderer: CSS2DRenderer = new CSS2DRenderer(); // CSS 2D渲染器
    private pmremGenerator: THREE.PMREMGenerator | undefined; // 环境贴图生成器

    private showSceneHelpers: boolean = true; // 是否显示场景辅助对象
    private grid: THREE.Group; // 网格组
    private box = new THREE.Box3(); // 包围盒
    private selectionBox: THREE.Box3Helper; // 选择框辅助对象
    private raycaster: THREE.Raycaster; // 射线投射器(用于鼠标拾取)

    private modules: {
        xr: XR,
        controls: CameraControls,
        transformControls: TransformControls,
        effect:ViewportEffect,
        viewCube: ViewCube,
        package: Package,
        registerSignal: ViewportSignals,
        shaderMaterialManager: ShaderMaterialManager
    };

    /**
     * 整个主场景的box3
     */
    sceneBox3 = new THREE.Box3();

    constructor(container: HTMLDivElement) {
        console.log("viewport init", container);
        // 初始化基本属性
        this.container = container;
        this.scene = window.editor.scene;
        this.camera = window.editor.camera;
        this.sceneHelpers = window.editor.sceneHelpers;

        /** helpers 创建网格并添加到场景 **/
        this.grid = new THREE.Group();
        this.grid.ignore = true; // 自定义属性，用于射线检测时忽略网格
        this.initGrid();
        this.scene.add(this.grid);

        /** 选中时的包围框 **/
        this.selectionBox = new THREE.Box3Helper(this.box); // 初始化选择框
        (this.selectionBox.material as THREE.Material).depthTest = false; // 禁用深度测试，确保选择框始终可见
        (this.selectionBox.material as THREE.Material).transparent = true; // 启用透明
        this.selectionBox.visible = false;
        // @ts-ignore
        this.sceneHelpers.add(this.selectionBox);

        // 拾取对象
        this.raycaster = new THREE.Raycaster(); // 初始化射线投射器
        //Raycaster 将只从它遇到的第一个对象中获取信息
        //this.raycaster.firstHitOnly = true;

        // 初始化所有模块
        this.modules = this.initModules();

        // 绑定事件监听
        this.container.addEventListener('mousedown', this.onMouseDown.bind(this));
        this.container.addEventListener('touchstart', this.onTouchStart.bind(this));
        this.container.addEventListener('dblclick', this.onDoubleClick.bind(this));
    }

    render() {
        if (!this.renderer) return;

        startTime = performance.now();
        const deltaTime = endTime - startTime;

        this.renderer.clearDepth(); // 清除深度缓冲

        if(this.modules.effect.enabled){
            this.modules.effect.render(deltaTime); // 使用后期处理效果渲染
        }else{
            this.renderer.render(this.scene, window.editor.viewportCamera); // 直接渲染
        }

        // 非默认相机不渲染辅助 // 渲染辅助场景（仅在使用默认相机时）
        if (this.camera === window.editor.viewportCamera) {
            if (this.showSceneHelpers) this.renderer.render(this.sceneHelpers, this.camera);
        }

        // 渲染CSS 2D内容
        this.css2DRenderer.render(this.scene, window.editor.viewportCamera);
        // 渲染CSS 3D内容
        this.css3DRenderer.render(this.scene, window.editor.viewportCamera);
        // this.css3DRenderer.render(this.sceneHelpers, window.editor.viewportCamera);
        
        // 计算渲染时间并发送信号
        endTime = performance.now();
        useDispatchSignal("sceneRendered", endTime - startTime);
    }

    protected initRenderer(newRenderer: THREE.WebGLRenderer) {
        // 清理旧渲染器
        if (this.renderer) {
            this.renderer.setAnimationLoop(null); // 停止动画循环
            this.renderer.dispose(); // 释放资源
            this.pmremGenerator?.dispose(); // 释放环境贴图生成器
            this.modules.controls.disconnect(); // 断开控制器连接
            this.container.removeChild(this.renderer.domElement); // 移除渲染DOM
        }

        this.renderer = newRenderer;
        // 设置动画循环
        this.renderer.setAnimationLoop(this.animate.bind(this));

        /*
        // 根据系统主题设置网格颜色
        if (window.matchMedia) {
            const grid1 = this.grid.children[0];
            const grid2 = this.grid.children[1];

            const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
            mediaQuery.addEventListener('change', (event) => {
                this.renderer?.setClearColor(event.matches ? 0x333333 : 0xaaaaaa);
                updateGridColors(grid1, grid2, event.matches ? GRID_COLORS_DARK : GRID_COLORS_LIGHT);
                this.render();
            });

            this.renderer.setClearColor(mediaQuery.matches ? 0x333333 : 0xAAAAAA);
            updateGridColors(grid1, grid2, mediaQuery.matches ? GRID_COLORS_DARK : GRID_COLORS_LIGHT);
        }
        */

        // 设置渲染器像素比和尺寸
        this.renderer.setPixelRatio(Math.max(Math.ceil(window.devicePixelRatio), 1));
        this.renderer.setSize(this.container.offsetWidth, this.container.offsetHeight);

        // 创建一个PMREMGenerator，从立方体映射环境纹理生成预过滤的 Mipmap 辐射环境贴图
        // 初始化环境贴图生成器
        this.pmremGenerator = new THREE.PMREMGenerator(this.renderer);
        this.pmremGenerator.compileEquirectangularShader();

        // 在container中最前面插入渲染器的dom元素
        // 添加渲染器DOM到容器
        this.container.insertBefore(this.renderer.domElement, this.container.firstChild);

        // 控制器绑定
        this.modules.controls.connect(this.renderer.domElement);

        // 加载默认环境贴图和背景
        this.loadDefaultEnvAndBackground();

        // 初始化后期处理效果
        if(this.modules.effect.enabled){
            this.modules.effect.createComposer();
        }

        // 防止重复添加
        if (this.css2DRenderer.domElement.parentNode!== this.container) {
            console.log("设置CSS 2D渲染器");
            this.css2DRenderer.setSize(this.container.offsetWidth, this.container.offsetHeight);
            this.css2DRenderer.domElement.setAttribute("id", "astral-3d-css2DRenderer");
            this.css2DRenderer.domElement.style.position = 'absolute';
            this.css2DRenderer.domElement.style.top = '0px';
            this.css2DRenderer.domElement.style.pointerEvents = 'none';

            this.container.appendChild(this.css2DRenderer.domElement);
        }




        //设置CSS 3D渲染器
        if (this.css3DRenderer.domElement.parentNode !== this.container) {
            console.log("设置CSS 3D渲染器");
            this.css3DRenderer.setSize(this.container.offsetWidth, this.container.offsetHeight);
            this.css3DRenderer.domElement.setAttribute("id", "astral-3d-css3DRenderer");
            this.css3DRenderer.domElement.style.position = 'absolute';
            this.css3DRenderer.domElement.style.top = '0px';
            this.css3DRenderer.domElement.style.pointerEvents = 'none';

            this.container.appendChild(this.css3DRenderer.domElement);
        }

        this.render();
    }

    protected initGrid() {
        // 创建一个1000x1000的网格辅助对象，网格密度为1000x1000
        const grid1 = new THREE.GridHelper(1000, 1000);
        // @ts-ignore
        // 设置网格颜色为浅灰色
        grid1.material.color.setHex(0x999999);
        // 禁用顶点颜色
        (grid1.material as THREE.Material).vertexColors = false;
        // 将网格添加到网格组中
        this.grid.add(grid1);


        const grid2 = new THREE.GridHelper(1000, 2, 0x7FE7C4, 0x777777);
        /*
        // 创建一个1000x1000的网格辅助对象，但网格密度为2x2
        const grid2 = new THREE.GridHelper(1000, 2);
        // const grid2 = new THREE.GridHelper(1000, 100);
        // @ts-ignore
        // 设置网格颜色为深灰色
        // grid2.material.color.setHex(0x777777);
        grid2.material.color.setHex(0x7FE7C4);
        */
        // 禁用顶点颜色
        (grid2.material as THREE.Material).vertexColors = true;
        // 将网格添加到网格组中
        this.grid.add(grid2);
    }

    protected initModules() {
        // 创建相机控制器
        const controls = new CameraControls(this.camera);
        // 监听相机更新事件
        controls.addEventListener("update", () => {
            useDispatchSignal("cameraChanged", this.camera);
        });
        // 用于存储变换控制器操作前的对象状态
        let objectPositionOnDown = new THREE.Vector3(); //位置
        let objectRotationOnDown = new THREE.Euler(); //旋转
        let objectScaleOnDown = new THREE.Vector3(); //缩放

        // 创建变换控制器
        const transformControls = new TransformControls(this.camera, this.container);

        // 监听变换事件
        transformControls.addEventListener("change", () => {
            const object = transformControls.object;
            // 更新选择框
            if (object !== undefined) {
                this.box.setFromObject(object, true);
                // 发送对象改变信号
                useDispatchSignal("objectChanged", object);
            }

            this.render();
        })
        // 监听鼠标按下事件
        transformControls.addEventListener("mouseDown", () => {
            const object = transformControls.object as THREE.Object3D;

            // 记录对象当前状态
            objectPositionOnDown = object.position.clone();
            objectRotationOnDown = object.rotation.clone();
            objectScaleOnDown = object.scale.clone();
            // 禁用相机控制器
            this.modules.controls.enabled = false;
        })
        // 监听鼠标抬起事件
        transformControls.addEventListener("mouseUp", () => {
            const object = transformControls.object as THREE.Object3D;
            if (object !== undefined) {
                // 根据变换模式执行相应的命令
                switch (transformControls.getMode()) {
                    case 'translate': // 平移
                        if (!objectPositionOnDown.equals(object.position)) {
                            window.editor.execute(new SetPositionCommand(object, object.position, objectPositionOnDown));
                        }
                        break;
                    case 'rotate': // 旋转
                        if (!objectRotationOnDown.equals(object.rotation)) {
                            window.editor.execute(new SetRotationCommand(object, object.rotation, objectRotationOnDown));
                        }
                        break;
                    case 'scale': // 缩放
                        if (!objectScaleOnDown.equals(object.scale)) {
                            window.editor.execute(new SetScaleCommand(object, object.scale, objectScaleOnDown));
                        }
                        break;
                }
            }
            // 重新启用相机控制器
            this.modules.controls.enabled = true;
        })
        // 获取变换控制器的辅助对象并添加到辅助场景
        const gizmo = transformControls.getHelper();
        this.sceneHelpers.add(gizmo);

        // 返回初始化的所有模块
        return {
            xr: new XR(transformControls), // VR/AR支持
            controls, // 相机控制器
            transformControls, // 变换控制器
            effect:new ViewportEffect(this), // 后期处理效果
            viewCube: new ViewCube(this.camera, this.container, controls), // 视图立方体贴图
            package: new Package(), // 资源包
            // 注册signal
            registerSignal: new ViewportSignals(this), // 信号注册
            shaderMaterialManager: new ShaderMaterialManager() // 着色器材质管理
        }
    }

    /**
     * 计算整个场景的Box3
     */
    computedSceneBox3() {
        // 计算整个场景的包围盒
        this.sceneBox3.setFromObject(this.scene);
    }

    updateAspectRatio() {
        // 遍历所有相机
        for (const uuid in window.editor.cameras) {
            const camera = window.editor.cameras[uuid];
            // 计算容器的宽高比
            const aspect = this.container.offsetWidth / this.container.offsetHeight;
            // 如果是透视相机，更新其宽高比
            if (camera.isPerspectiveCamera) {
                camera.aspect = aspect;
            }
            // 更新相机的投影矩阵
            camera.updateProjectionMatrix();
            // 更新相机辅助对象
            const cameraHelper = window.editor.helpers[camera.id];
            if (cameraHelper) cameraHelper.update();
        }
    }

    /**
     * 加载环境和背景
     */
    loadDefaultEnvAndBackground() {
        window.editor.resource.loadURLTexture(`/static/resource/hdr/kloofendal_48d_partly_cloudy_puresky_1k.hdr`, (texture: THREE.Texture) => {
            // 设置贴图映射方式为等距柱状投影反射映射
            texture.mapping = THREE.EquirectangularReflectionMapping;
            // 设置为场景环境贴图
            this.scene.environment = texture;
            // 设置为场景背景
            this.scene.background = texture;
            // 发送场景图变化信号
            useDispatchSignal("sceneGraphChanged");
        })
    }

    /**
     * 射线检测
     */
    getIntersects(point: THREE.Vector2) {
        const mouse = new THREE.Vector2();
        // 将屏幕坐标转换为标准化设备坐标(-1到1)
        mouse.set((point.x * 2) - 1, -(point.y * 2) + 1);
        // 设置射线起点和方向
        this.raycaster.setFromCamera(mouse, this.camera);

        // 收集场景中的可见对象
        const objects: THREE.Object3D[] = [];
        // 遍历场景中的对象，排除被忽略和不可见的对象
        this.scene.traverseByCondition(function (child) {
            objects.push(child);
        }, (child) => !child.ignore && child.visible);

        // 添加辅助场景中的picker对象
        this.sceneHelpers.traverseVisible(function (child) {
            if (child.name === 'picker') objects.push(child);
        });
        // 返回射线与对象的交点信息
        return this.raycaster.intersectObjects(objects, false);
    }

    handleClick() {
        console.log("Viewport handleClick");
        // 检查鼠标按下和抬起的位置是否相同（判断是否为点击而不是拖拽）
        if (onDownPosition.distanceTo(onUpPosition) === 0) {
            // 获取射线与场景物体的交点
            const intersects = this.getIntersects(onUpPosition);
            // 发送交点检测信号，通知其他组件处理点击事件
            useDispatchSignal("intersectionsDetected", intersects);
            // 重新渲染场景
            this.render();
        }
    }

    onMouseDown(event: MouseEvent) {
        event.preventDefault();
        // 获取鼠标在容器中的相对位置
        const array = getMousePosition(this.container, event.clientX, event.clientY);
        // 记录鼠标按下时的位置
        onDownPosition.fromArray(array);
        // 绑定鼠标抬起事件处理函数
        mouseUpFn = this.onMouseUp.bind(this);
        document.addEventListener('mouseup', mouseUpFn);
    }

    onMouseUp(event: MouseEvent) {
        // 获取鼠标抬起时的位置
        const array = getMousePosition(this.container, event.clientX, event.clientY);
        // 记录鼠标抬起位置
        onUpPosition.fromArray(array);
        // 处理点击事件
        this.handleClick();
        // 移除鼠标抬起事件监听
        document.removeEventListener('mouseup', mouseUpFn);
        mouseUpFn = undefined;
    }

    onTouchStart(event: TouchEvent) {
        // 获取第一个触摸点
        const touch = event.changedTouches[0];
        // 获取触摸点在容器中的位置
        const array = getMousePosition(this.container, touch.clientX, touch.clientY);
        // 记录触摸开始位置
        onDownPosition.fromArray(array);
        // 绑定触摸结束事件处理函数
        mouseUpFn = this.onTouchEnd.bind(this);
        document.addEventListener('touchend', mouseUpFn);
    }

    onTouchEnd(event: TouchEvent) {
        // 获取第一个触摸点
        const touch = event.changedTouches[0];
        // 获取触摸结束时的位置
        const array = getMousePosition(this.container, touch.clientX, touch.clientY);
        // 记录触摸结束位置
        onUpPosition.fromArray(array);
        // 处理点击事件
        this.handleClick();
        // 移除触摸结束事件监听
        document.removeEventListener('touchend', mouseUpFn);
        mouseUpFn = undefined;
    }

    onDoubleClick(event: MouseEvent) {
        // 获取双击时的鼠标位置
        const array = getMousePosition(this.container, event.clientX, event.clientY);
        // 记录双击位置
        onDoubleClickPosition.fromArray(array);
        // 获取射线与场景物体的交点
        const intersects = this.getIntersects(onDoubleClickPosition);
        // 如果有交点，发送对象聚焦信号
        if (intersects.length > 0) {
            const intersect = intersects[0];
            useDispatchSignal("objectFocused", intersect.object);
        }
    }

    /**
     * 动画循坏
     */
    animate() {
        const mixer = window.editor.mixer;
        const delta = clock.getDelta(); // 获取时间增量

        // 更新相机控制器
        let needsUpdate = this.modules.controls.update(delta);

        // Animations 处理动画
        const actions = mixer.stats.actions;
        if (actions.inUse > 0 || prevActionsInUse > 0) {
            prevActionsInUse = actions.inUse;

            mixer.update(delta); // 更新动画混合器
            needsUpdate = true;

            if (window.editor.selected !== null) {
                // 避免某些蒙皮网格的帧延迟效应(e.g. Michelle.glb)
                // 更新选中对象的世界矩阵
                window.editor.selected.updateWorldMatrix(false, true);
                //  选择框应反映当前动画状态
                // 更新选择框
                this.selectionBox.box.setFromObject(window.editor.selected, true);
            }
        }
        // 更新其他模块
        this.modules.viewCube.update();
        this.modules.shaderMaterialManager.update();
        if (this.modules.shaderMaterialManager.needRender) {
            needsUpdate = true;
        }

        if (this.renderer?.xr.isPresenting) {
            needsUpdate = true;
        }
        // 如果需要更新，则重新渲染
        if (needsUpdate) this.render();
    }

}

function updateGridColors(grid1, grid2, colors) {
    grid1.material.color.setHex(colors[0]);
    grid2.material.color.setHex(colors[1]);
}